/**
 * 
 */
package truerefactor.input;

import japa.parser.ast.CompilationUnit;
import japa.parser.ast.PackageDeclaration;
import japa.parser.ast.body.BodyDeclaration;
import japa.parser.ast.body.ClassOrInterfaceDeclaration;
import japa.parser.ast.body.ConstructorDeclaration;
import japa.parser.ast.body.FieldDeclaration;
import japa.parser.ast.body.MethodDeclaration;
import japa.parser.ast.body.ModifierSet;
import japa.parser.ast.body.Parameter;
import japa.parser.ast.body.TypeDeclaration;
import japa.parser.ast.body.VariableDeclarator;
import japa.parser.ast.expr.AnnotationExpr;
import japa.parser.ast.expr.ArrayAccessExpr;
import japa.parser.ast.expr.ArrayCreationExpr;
import japa.parser.ast.expr.ArrayInitializerExpr;
import japa.parser.ast.expr.AssignExpr;
import japa.parser.ast.expr.BinaryExpr;
import japa.parser.ast.expr.BooleanLiteralExpr;
import japa.parser.ast.expr.CastExpr;
import japa.parser.ast.expr.CharLiteralExpr;
import japa.parser.ast.expr.ClassExpr;
import japa.parser.ast.expr.ConditionalExpr;
import japa.parser.ast.expr.DoubleLiteralExpr;
import japa.parser.ast.expr.EnclosedExpr;
import japa.parser.ast.expr.Expression;
import japa.parser.ast.expr.FieldAccessExpr;
import japa.parser.ast.expr.InstanceOfExpr;
import japa.parser.ast.expr.IntegerLiteralExpr;
import japa.parser.ast.expr.IntegerLiteralMinValueExpr;
import japa.parser.ast.expr.LiteralExpr;
import japa.parser.ast.expr.LongLiteralExpr;
import japa.parser.ast.expr.LongLiteralMinValueExpr;
import japa.parser.ast.expr.MarkerAnnotationExpr;
import japa.parser.ast.expr.MethodCallExpr;
import japa.parser.ast.expr.NameExpr;
import japa.parser.ast.expr.NormalAnnotationExpr;
import japa.parser.ast.expr.NullLiteralExpr;
import japa.parser.ast.expr.ObjectCreationExpr;
import japa.parser.ast.expr.QualifiedNameExpr;
import japa.parser.ast.expr.SingleMemberAnnotationExpr;
import japa.parser.ast.expr.StringLiteralExpr;
import japa.parser.ast.expr.SuperExpr;
import japa.parser.ast.expr.SuperMemberAccessExpr;
import japa.parser.ast.expr.ThisExpr;
import japa.parser.ast.expr.UnaryExpr;
import japa.parser.ast.expr.VariableDeclarationExpr;
import japa.parser.ast.stmt.BlockStmt;
import japa.parser.ast.stmt.CatchClause;
import japa.parser.ast.stmt.DoStmt;
import japa.parser.ast.stmt.ExpressionStmt;
import japa.parser.ast.stmt.ForeachStmt;
import japa.parser.ast.stmt.IfStmt;
import japa.parser.ast.stmt.ReturnStmt;
import japa.parser.ast.stmt.Statement;
import japa.parser.ast.stmt.SwitchEntryStmt;
import japa.parser.ast.stmt.SwitchStmt;
import japa.parser.ast.stmt.SynchronizedStmt;
import japa.parser.ast.stmt.ThrowStmt;
import japa.parser.ast.stmt.TryStmt;
import japa.parser.ast.stmt.TypeDeclarationStmt;
import japa.parser.ast.stmt.WhileStmt;
import japa.parser.ast.type.ClassOrInterfaceType;
import japa.parser.ast.type.PrimitiveType;
import japa.parser.ast.type.ReferenceType;
import japa.parser.ast.type.Type;
import japa.parser.ast.type.VoidType;
import japa.parser.ast.type.WildcardType;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import truerefactor.graph.Accessibility;
import truerefactor.graph.AttributeNode;
import truerefactor.graph.ClassNode;
import truerefactor.graph.CodeGraph;
import truerefactor.graph.CodeNode;
import truerefactor.graph.Edge;
import truerefactor.graph.EdgeType;
import truerefactor.graph.MethodNode;
import truerefactor.graph.ModifierParams;
import truerefactor.graph.PackageNode;
import truerefactor.graph.StatementNode;
import truerefactor.graph.StatementType;
import truerefactor.graph.TypePair;

/**
 * @author Isaac
 */
public class JavaGraphParser extends GraphParser {

    /**
     * The specific language this graph parser was designed to handle
     */
    public static final String LANGUAGE = "Java";
    /**
     * The extension of files this graph parser uses
     */
    public static final String EXT = ".java";
    /**
     * The current package the graph parser is exploring
     */
    private PackageNode currentPackage = null;

    /*
     * (non-Javadoc)
     * @see truerefactor.input.GraphParser#start(java.lang.Object)
     */
    @Override
    public void start(Object type)
    {
        if (type instanceof ClassOrInterfaceDeclaration)
        {
            ClassOrInterfaceDeclaration cid = (ClassOrInterfaceDeclaration) type;

            String className = cid.getName();
            ClassNode cNode = getClassNode(className);

            processTypeDeclaration(cid, cNode);
        }
    }

    /**
     * FIXME: Requires a massive refactoring to get rid of the if statements
     */
    /*
     * (non-Javadoc)
     * @see truerefactor.input.GraphParser#buildConnections(japa.parser.ast.
     * CompilationUnit)
     */
    @Override
    public void buildConnections(CompilationUnit cu)
    {
        PackageDeclaration packDec = cu.getPakage();
        // System.out.println(packDec.getName().toString());

        if (packDec == null)
        {
            currentPackage = PackageNode.getDefaultPackage();
        }
        else
        {
            currentPackage = addPackage(
                    cu.getPakage().getName().toString(),
                    cu.getPakage().getName().getName(),
                    siphonCode(file, packDec.getBeginLine(), packDec.getBeginColumn(), packDec.getEndLine(),
                            packDec.getEndColumn()));
        }

        List<TypeDeclaration> types = cu.getTypes();

        for (TypeDeclaration type : types)
        {
            if (type instanceof ClassOrInterfaceDeclaration)
            {
                ClassOrInterfaceDeclaration cid = (ClassOrInterfaceDeclaration) type;

                ClassNode cNode = null;

                ModifierParams modify = getModifierParams(cid.getModifiers());

                cNode = addClass(cid.getName(), modify);
                cNode.setInterface(cid.isInterface());

                List<ClassOrInterfaceType> lstClasses = cid.getImplements();
                if (lstClasses != null)
                {
                    for (ClassOrInterfaceType cit : cid.getImplements())
                    {
                        String impl = cit.getName();
                        cNode.addImplements(impl);
                    }
                }

                cid.getJavaDoc();

                lstClasses = cid.getExtends();
                if (lstClasses != null)
                {
                    for (ClassOrInterfaceType cit : cid.getExtends())
                    {
                        String ext = cit.getName();
                        cNode.addExtends(ext);
                    }
                }

                processTypeDeclaration(cid, cNode);
            }
        }

    }

    /**
     * FIXME: Requires a massive refactoring to get rid of the if statements
     * Processes type declarations
     * 
     * @param cid
     *            Class or Interface Declaration to be processed
     * @param cNode
     *            The class node associated with the declaration
     */
    private void processTypeDeclaration(ClassOrInterfaceDeclaration cid, ClassNode cNode)
    {

        if (cNode != null)
        {
            List<BodyDeclaration> body = cid.getMembers();
            for (BodyDeclaration bodyDec : body)
            {
                if (bodyDec instanceof FieldDeclaration)
                { // Handle
                  // Fields

                    FieldDeclaration field = (FieldDeclaration) bodyDec;
                    String fieldType = handleType(field.getType());
                    ModifierParams modify = getModifierParams(field.getModifiers());

                    List<VariableDeclarator> variables = field.getVariables();
                    for (VariableDeclarator var : variables)
                    {
                        addAttribute(
                                cNode,
                                var.getId().getName(),
                                siphonCode(file, var.getBeginLine(), var.getBeginColumn(), var.getEndLine(),
                                        var.getEndColumn()), fieldType, modify);
                    }
                }
                else if (bodyDec instanceof MethodDeclaration)
                { // Handle
                  // Methods
                    MethodDeclaration method = (MethodDeclaration) bodyDec;

                    handleMethod(method, cNode);
                }
                else if (bodyDec instanceof ConstructorDeclaration)
                { // Handle
                  // Constructors
                    ConstructorDeclaration construct = (ConstructorDeclaration) bodyDec;

                    handleConstructor(construct, cNode);
                }
            }
        }
        else
        {
            String fullClassName;
            if (currentPackage == null)
            {
                fullClassName = cid.getName();
            }
            else
            {
                fullClassName = currentPackage.getFullName() + "." + cid.getName();
            }

            cNode = new ClassNode(fullClassName, siphonCode(file, cid.getBeginLine(), cid.getBeginColumn(),
                    cid.getEndLine(), cid.getEndColumn()));
        }
    }

    /**
     * Handles method declarations and the generation of new method nodes to be
     * associated with the provided parent class node
     * 
     * @param method
     *            the method declaration from which a new method node will be
     *            generated
     * @param cNode
     *            class node to which the method will be associated
     */
    private void handleMethod(MethodDeclaration method, ClassNode cNode)
    {
        int modifiers = method.getModifiers();
        String retType = handleType(method.getType());

        List<String> params = new ArrayList<String>();
        List<TypePair> paramInfo = new ArrayList<TypePair>();
        getParams(method.getParameters(), params, paramInfo);

        if (cNode == null)
            return;

        MethodNode mNode = addMethod(
                cNode,
                method.getName(),
                params,
                paramInfo,
                siphonCode(file, method.getBeginLine(), method.getBeginColumn(), method.getEndLine(),
                        method.getEndColumn()), retType, getModifierParams(modifiers));

        // Handle the method body
        BlockStmt methodBody = method.getBody();

        handleMethodContents(methodBody, mNode);
    }

    /**
     * Handles the processing of Type statements
     * 
     * @param type
     *            Type object to be used to determine the associated type
     * @return String value for the type.
     */
    private String handleType(Type type)
    {
        String retType = "";

        if (type instanceof PrimitiveType)
        {
            PrimitiveType pType = (PrimitiveType) type;
            retType = pType.getType().toString();
        }
        else if (type instanceof ClassOrInterfaceType)
        {
            ClassOrInterfaceType clType = (ClassOrInterfaceType) type;
            retType = clType.getName();
        }
        else if (type instanceof VoidType)
        {
            retType = "void";
        }
        else if (type instanceof WildcardType)
        {
            WildcardType wcType = (WildcardType) type;
            retType = handleType(wcType.getExtends().getType());
        }
        else if (type instanceof ReferenceType)
        {
            ReferenceType rType = (ReferenceType) type;
            retType = handleType(rType.getType());
        }

        return retType;
    }

    /**
     * Generates a set of modifier parameters for a given integer representation
     * 
     * @param modifiers
     *            the integer representation of the modifier param
     * @return ModifierParams object to be used in Class, Method, and Attribute
     *         creation
     */
    private ModifierParams getModifierParams(int modifiers)
    {
        boolean isStatic = false;
        boolean isAbstract = false;
        Accessibility access = Accessibility.PackageProtected;
        if (ModifierSet.isPrivate(modifiers))
            access = Accessibility.Private;
        else if (ModifierSet.isProtected(modifiers))
            access = Accessibility.Protected;
        else if (ModifierSet.isPublic(modifiers))
            access = Accessibility.Public;

        if (ModifierSet.isStatic(modifiers))
        {
            isStatic = true;
        }

        if (ModifierSet.isAbstract(modifiers))
        {
            isAbstract = true;
        }

        return new ModifierParams(isAbstract, isStatic, access);
    }

    /**
     * Handles the creation of constructor method nodes from the constructor
     * declaration.
     * 
     * @param construct
     *            Declaration describing the constructor
     * @param cNode
     *            Parent class node to which the constructor will be associated
     *            with
     */
    private void handleConstructor(ConstructorDeclaration construct, ClassNode cNode)
    {
        BlockStmt methodBody = construct.getBlock();

        int modifiers = construct.getModifiers();
        Accessibility access = Accessibility.PackageProtected;
        if (ModifierSet.isPrivate(modifiers))
            access = Accessibility.Private;
        else if (ModifierSet.isProtected(modifiers))
            access = Accessibility.Protected;
        else if (ModifierSet.isPublic(modifiers))
            access = Accessibility.Public;

        List<String> params = new ArrayList<String>();
        List<TypePair> paramInfo = new ArrayList<TypePair>();
        getParams(construct.getParameters(), params, paramInfo);

        if (cNode == null)
            return;

        MethodNode node = addMethod(
                cNode,
                construct.getName(),
                params,
                paramInfo,
                siphonCode(file, construct.getBeginLine(), construct.getBeginColumn(), construct.getEndLine(),
                        construct.getEndColumn()), "CONSTRUCTOR", new ModifierParams(false, false, access));

        handleMethodContents(methodBody, node);
    }

    /**
     * Handles the development of method statement graphs from a method body
     * block stmt
     * 
     * @param methodBody
     *            body of the method to be broken into a statement graph
     * @param node
     *            parent method node
     */
    private void handleMethodContents(BlockStmt methodBody, MethodNode node)
    {
        if (methodBody == null)
            return;

        List<Statement> statements = methodBody.getStmts();
        if (statements != null)
        {
            for (Statement stmt : statements)
            {
                processStatement(stmt, node, node.getStatementGraph());
            }
        }
    }

    /**
     * Generates a new Statement Node from a Statement Object
     * 
     * @param stmt
     *            Statement Object to transformed into a StatementNode
     * @param node
     *            previous StatementNode
     * @return current StatementGraph
     */
    private StatementNode handleBodyStatements(Statement stmt, StatementNode node, CodeGraph stmtGraph)
    {
        return processStatement(stmt, node, stmtGraph);
    }

    /**
     * FIXME: Requires a massive refactoring to get rid of the if statements
     * Handles converting expressions into portions of statement nodes
     * 
     * @param expr
     *            expression to be transformed
     * @param parent
     *            parent Code Node the expression is to be associated with
     * @return StatementNode the generated StatementNode will be a part of
     */
    @SuppressWarnings("unused")
    private StatementNode handleExpressionContents(Expression expr, CodeNode parent, CodeGraph stmtGraph)
    {
        if (expr == null)
        {
            return null;
        }

        StatementNode sNode = addStatement(parent, stmtGraph, StatementType.EXPRESSION,
                siphonCode(file, expr.getBeginLine(), expr.getBeginColumn(), expr.getEndLine(), expr.getEndColumn()));

        StatementNode retVal = sNode;

        if (expr instanceof AnnotationExpr)
        {
            AnnotationExpr annotate = (AnnotationExpr) expr;

            handleAnnotation(annotate, parent);
        }
        else if (expr instanceof ArrayAccessExpr)
        {
            ArrayAccessExpr array = (ArrayAccessExpr) expr;
            // String varName = array.getName().getData().toString();
            //
            // if (parent instanceof MethodNode)
            // {
            // MethodNode mNode = (MethodNode) parent;
            // ClassNode cNode = mNode.getParentClass();
            //
            // AttributeNode aNode = cNode.getAttribute(varName);
            // if (aNode != null)
            // {
            // mNode.addAssociatedAttribute(aNode);
            // graph.insert(new Edge(mNode, aNode,
            // EdgeType.IntraClassAssociation));
            // }
            // }
            retVal = handleExpressionContents(array.getName(), sNode, stmtGraph);
        }
        else if (expr instanceof ArrayCreationExpr)
        {
            ArrayCreationExpr array = (ArrayCreationExpr) expr;

            String typeName = handleType(array.getType());
            if (getClassNode(typeName) != null)
            {
                if (parent instanceof MethodNode)
                {
                    MethodNode method = (MethodNode) parent;
                    ClassNode cNode = method.getParentClass();

                    cNode.addAssociatedClass(getClassNode(typeName));
                }
                else if (parent instanceof StatementNode)
                {
                    MethodNode method = ((StatementNode) parent).getParent();
                    ClassNode cNode = method.getParentClass();

                    cNode.addAssociatedClass(getClassNode(typeName));
                }
            }
        }
        else if (expr instanceof ArrayInitializerExpr)
        {
            ArrayInitializerExpr array = (ArrayInitializerExpr) expr;
        }
        else if (expr instanceof AssignExpr)
        {
            AssignExpr assign = (AssignExpr) expr;
            retVal = handleExpressionContents(assign.getTarget(), sNode, stmtGraph);
        }
        else if (expr instanceof BinaryExpr)
        {
            BinaryExpr binary = (BinaryExpr) expr;
        }
        else if (expr instanceof CastExpr)
        {
            CastExpr cast = (CastExpr) expr;
        }
        else if (expr instanceof ClassExpr)
        {
            ClassExpr classEx = (ClassExpr) expr;
        }
        else if (expr instanceof ConditionalExpr)
        {
            ConditionalExpr cond = (ConditionalExpr) expr;
        }
        else if (expr instanceof EnclosedExpr)
        {
            EnclosedExpr enclose = (EnclosedExpr) expr;

        }
        else if (expr instanceof FieldAccessExpr)
        {
            FieldAccessExpr field = (FieldAccessExpr) expr;

            if (parent instanceof StatementNode)
            {
                MethodNode mNode = ((StatementNode) parent).getParent();
                if (mNode == null)
                    return retVal;

                ClassNode cNode = mNode.getParentClass();

                AttributeNode fNode = cNode.getAttribute(field.getField());
                if (fNode != null)
                {
                    if (fNode.getParent().equals(mNode.getParentClass()))
                    {
                        mNode.addAssociatedAttribute(fNode);
                        fNode.addAssociatedClassMethod(mNode);
                    }
                    else
                    {
                        mNode.addOtherAttribute(fNode);
                        fNode.addAssociatedOtherMethod(mNode);
                    }
                }
            }
            else if (parent instanceof MethodNode)
            {
                MethodNode mNode = (MethodNode) parent;
                ClassNode cNode = mNode.getParentClass();

                AttributeNode fNode = cNode.getAttribute(field.getField());
                if (fNode != null)
                {
                    if (fNode.getParent().equals(mNode.getParentClass()))
                    {
                        mNode.addAssociatedAttribute(fNode);
                        fNode.addAssociatedClassMethod(mNode);
                    }
                    else
                    {
                        mNode.addOtherAttribute(fNode);
                        fNode.addAssociatedOtherMethod(mNode);
                    }

                }
            }
        }
        else if (expr instanceof InstanceOfExpr)
        {
            InstanceOfExpr ioEx = (InstanceOfExpr) expr;
        }
        else if (expr instanceof LiteralExpr)
        {
            LiteralExpr litEx = (LiteralExpr) expr;
            handleLiteralExpression(litEx, parent);
        }
        else if (expr instanceof MethodCallExpr)
        {
            String className = "";

            MethodCallExpr methodCall = (MethodCallExpr) expr;

            if (methodCall.getScope() instanceof ClassExpr)
            {
                ClassExpr ex = (ClassExpr) methodCall.getScope();
                if (ex.getType() instanceof ClassOrInterfaceType)
                {
                    className = ((ClassOrInterfaceType) ex.getType()).getName();
                }
            }

            MethodNode callNode = getMethodNode(className, methodCall.getName(),
                    getParamTypes(methodCall.getTypeArgs()));
            if (callNode != null)
            {
                if (parent instanceof StatementNode)
                {
                    MethodNode mNode = ((StatementNode) parent).getParent();
                    ClassNode cNode = mNode.getParentClass();
                    if (cNode.getIdentifier().equals(callNode.getParentClass().getIdentifier()))
                    {
                        mNode.addAssociatedMethod(callNode);
                    }
                    else
                    {
                        mNode.addOtherMethod(callNode);
                    }
                }
                else if (parent instanceof MethodNode)
                {
                    MethodNode mNode = (MethodNode) parent;
                    ClassNode cNode = mNode.getParentClass();
                    if (cNode.getIdentifier().equals(callNode.getParentClass().getIdentifier()))
                    {
                        mNode.addAssociatedMethod(callNode);
                    }
                    else
                    {
                        mNode.addOtherMethod(callNode);
                    }
                }
            }
        }
        else if (expr instanceof NameExpr)
        {
            NameExpr nameEx = (NameExpr) expr;
        }
        else if (expr instanceof ObjectCreationExpr)
        {
            ObjectCreationExpr objCreate = (ObjectCreationExpr) expr;

            ClassOrInterfaceType cit = objCreate.getType();
            String className = cit.getName();

            // MethodNode mNode = (MethodNode) parent;
            // ClassNode cNode = getClassNode(cit.getName());
        }
        else if (expr instanceof QualifiedNameExpr)
        {
            QualifiedNameExpr qual = (QualifiedNameExpr) expr;
        }
        else if (expr instanceof SuperExpr)
        {
            SuperExpr superEx = (SuperExpr) expr;
        }
        else if (expr instanceof SuperMemberAccessExpr)
        {
            SuperMemberAccessExpr superMemeber = (SuperMemberAccessExpr) expr;
        }
        else if (expr instanceof ThisExpr)
        {
            ThisExpr thisEx = (ThisExpr) expr;
        }
        else if (expr instanceof UnaryExpr)
        {
            UnaryExpr unary = (UnaryExpr) expr;
        }
        else if (expr instanceof VariableDeclarationExpr)
        {
            VariableDeclarationExpr varDec = (VariableDeclarationExpr) expr;

            List<VariableDeclarator> lstVars = varDec.getVars();
            for (VariableDeclarator declarator : lstVars)
            {
                declarator.getId().getName();
            }
        }

        return retVal;
    }

    /**
     * FIXME: Requires a massive refactoring to get rid of the if statements
     * Handles the case that a literal is found.
     * 
     * @param expr
     *            expression object
     * @param parent
     *            parent to which the generate statement will be associated with
     */
    @SuppressWarnings("unused")
    private void handleLiteralExpression(LiteralExpr expr, CodeNode parent)
    {
        if (expr instanceof BooleanLiteralExpr)
        {
            BooleanLiteralExpr bool = (BooleanLiteralExpr) expr;

        }
        else if (expr instanceof StringLiteralExpr)
        {
            StringLiteralExpr stringLit = (StringLiteralExpr) expr;

        }
        else if (expr instanceof NullLiteralExpr)
        {
            NullLiteralExpr nullLit = (NullLiteralExpr) expr;
        }
    }

    /**
     * FIXME: Requires a massive refactoring to get rid of the if statements
     * Handles String Literal expressions
     * 
     * @param expr
     *            expression to be handled
     * @param parent
     *            code node to associate the statement to
     */
    @SuppressWarnings("unused")
    private void handleStringLiteralExpression(StringLiteralExpr expr, CodeNode parent)
    {
        if (expr instanceof LongLiteralExpr)
        {
            LongLiteralExpr longLit = (LongLiteralExpr) expr;

        }
        else if (expr instanceof LongLiteralMinValueExpr)
        {
            LongLiteralMinValueExpr minLong = (LongLiteralMinValueExpr) expr;

        }
        else if (expr instanceof CharLiteralExpr)
        {

        }
        else if (expr instanceof IntegerLiteralExpr)
        {
            IntegerLiteralExpr intLit = (IntegerLiteralExpr) expr;

        }
        else if (expr instanceof IntegerLiteralMinValueExpr)
        {
            IntegerLiteralMinValueExpr minInt = (IntegerLiteralMinValueExpr) expr;

        }
        else if (expr instanceof DoubleLiteralExpr)
        {
            DoubleLiteralExpr doulbeLit = (DoubleLiteralExpr) expr;

        }
    }

    /**
     * Handles the potential of running into an annotation.
     * 
     * @param expr
     *            Annotation expression object
     * @param parent
     *            parent code node
     */
    @SuppressWarnings("unused")
    private void handleAnnotation(AnnotationExpr expr, CodeNode parent)
    {
        if (expr instanceof MarkerAnnotationExpr)
        {
            MarkerAnnotationExpr annotate = (MarkerAnnotationExpr) expr;

        }
        else if (expr instanceof NormalAnnotationExpr)
        {
            NormalAnnotationExpr annotate = (NormalAnnotationExpr) expr;

        }
        else if (expr instanceof SingleMemberAnnotationExpr)
        {
            SingleMemberAnnotationExpr single = (SingleMemberAnnotationExpr) expr;

        }
    }

    /**
     * FIXME: Requires a massive refactoring to get rid of the if statements
     * Handles type declarations with in statements.
     * 
     * @param typeDec
     *            the new declaration
     * @param node
     *            the previous statement node
     */
    private void handleTypeDeclaration(TypeDeclaration typeDec, StatementNode node)
    {
        List<BodyDeclaration> decs = typeDec.getMembers();

        for (BodyDeclaration type : decs)
        {
            if (type instanceof ClassOrInterfaceDeclaration)
            {
                ClassOrInterfaceDeclaration cid = (ClassOrInterfaceDeclaration) type;

                String className = cid.getName();
                ClassNode cNode = getClassNode(className);
                if (cNode != null)
                {
                    List<BodyDeclaration> body = cid.getMembers();
                    for (BodyDeclaration bd : body)
                    {
                        if (bd instanceof FieldDeclaration)
                        { // Handle Fields

                            FieldDeclaration field = (FieldDeclaration) bd;
                            String fieldType = handleType(field.getType());
                            ModifierParams modify = getModifierParams(field.getModifiers());

                            List<VariableDeclarator> variables = field.getVariables();
                            for (VariableDeclarator var : variables)
                            {
                                addAttribute(
                                        cNode,
                                        var.getId().getName(),
                                        siphonCode(file, var.getBeginLine(), var.getBeginColumn(), var.getEndLine(),
                                                var.getEndColumn()), fieldType, modify);
                            }
                        }
                        else if (bd instanceof MethodDeclaration)
                        { // Handle
                          // Methods
                            MethodDeclaration method = (MethodDeclaration) bd;

                            handleMethod(method, cNode);
                        }
                        else if (bd instanceof ConstructorDeclaration)
                        { // Handle
                          // Constructors
                            ConstructorDeclaration construct = (ConstructorDeclaration) bd;

                            handleConstructor(construct, cNode);
                        }
                    }
                }
            }
        }

    }

    /**
     * Finds a class node with the same name as the string provided
     * 
     * @param className
     *            search string for a class
     * @return ClassNode with the same name if one exists in the current state
     *         of the CodeGraph or null if not.
     */
    private ClassNode getClassNode(String className)
    {
        return graph.getClass(className);
    }

    /**
     * Returns a MethodNode with the same signature as the one describe by the
     * provide parameters.
     * 
     * @param ClassName
     *            ClassName of the class node the method is supposedly contained
     *            within
     * @param methodName
     *            name of the method to be found
     * @param params
     *            parameter information of the method
     * @return MethodNode described by the search parameters, or null if no
     *         results
     */
    private MethodNode getMethodNode(String ClassName, String methodName, List<TypePair> params)
    {
        ClassNode cNode = graph.getClass(ClassName);
        if (cNode != null)
            return graph.getMethod(cNode, methodName, params);

        else
            return null;
    }

    /*
     * (non-Javadoc)
     * @see
     * truerefactor.input.GraphParser#addAttribute(truerefactor.graph.ClassNode,
     * java.lang.String, java.lang.String)
     */
    @Override
    public AttributeNode addAttribute(ClassNode cNode, String fieldName, String code, String type,
            ModifierParams modifiers)
    {
        AttributeNode aNode = new AttributeNode(cNode, fieldName, code);
        aNode.setStatic(modifiers.isStatic());
        aNode.setType(type);
        aNode.setAccessibility(modifiers.getAccessibility());
        aNode.setParent(cNode);
        cNode.addAttribute(aNode);

        ClassNode assoc = getClassNode(type);
        if (assoc != null)
        {
            cNode.addAssociatedClass(assoc);
        }

        graph.insert(aNode);

        return aNode;
    }

    /*
     * (non-Javadoc)
     * @see
     * truerefactor.input.GraphParser#addMethod(truerefactor.graph.ClassNode,
     * java.lang.String, java.lang.String[], java.lang.String)
     */
    @Override
    public MethodNode addMethod(ClassNode cNode, String methodName, List<String> params, List<TypePair> paramInfo,
            String code, String type, ModifierParams modifiers)
    {
        assert (cNode != null);
        MethodNode mNode = new MethodNode(methodName, code, modifiers.getAccessibility());
        mNode.setParentClass(cNode);

        // FIXME: how can paramInfo and params not be the same size
        if (!params.isEmpty() && !paramInfo.isEmpty())
        {
            for (int i = 0; i < params.size(); i++)
            {
                mNode.addParameter(params.get(i), paramInfo.get(i));
            }
        }

        if (!type.equals("void") && !type.equals("CONSTRUCTOR"))
            mNode.setReturnType(type);

        mNode.setStatic(modifiers.isStatic());
        mNode.setAbstract(modifiers.isAbstract());
        graph.insert(mNode);

        return mNode;
    }

    /*
     * (non-Javadoc)
     * @see truerefactor.input.GraphParser#addPackage(java.lang.String)
     */
    @Override
    public PackageNode addPackage(String fullName, String packageName, String code)
    {
        String base = "";
        int index = fullName.indexOf(packageName);
        if (index > 0)
            base = fullName.substring(0, index - 1);

        PackageNode retVal = graph.getPackage(packageName);

        if (retVal == null)
        {
            retVal = new PackageNode(packageName, code);
            graph.insert(retVal);
        }

        retVal.setBase(base);

        return retVal;
    }

    /*
     * (non-Javadoc)
     * @see truerefactor.input.GraphParser#addClass(java.lang.String,
     * java.lang.String)
     */
    @Override
    public ClassNode addClass(String className, ModifierParams modifier)
    {
        ClassNode retVal = new ClassNode(CodeGraph.nextUID(), className, "", currentPackage,
                modifier.getAccessibility());
        retVal.setAbstract(modifier.isAbstract());
        graph.insert(retVal);

        return retVal;
    }

    /**
     * Method used to extract the actual code from the file based on information
     * provided by the parser
     * 
     * @param file
     *            the file in which to extract the code from
     * @param beginLine
     *            beginning line of the file where the code to extract starts
     * @param beginCol
     *            beginning column of the beginning line where the code to
     *            extract starts
     * @param endLine
     *            line in the file where the code ends
     * @param endCol
     *            column of the endLine where the code ends
     * @return String representation of the extracted code.
     */
    private String siphonCode(File file, int beginLine, int beginCol, int endLine, int endCol)
    {
        int line = 1;
        StringBuffer sb = new StringBuffer();

        try
        {
            BufferedReader br = new BufferedReader(new FileReader(file));
            String ln = "";

            while ((ln = br.readLine()) != null)
            {
                if (line > endLine)
                    break;
                if (line < beginLine)
                {
                    line++;
                    continue;
                }

                if (line >= beginLine && line <= endLine)
                {
                    if (beginLine == endLine)
                    {
                        sb.append(ln);
                        break;
                    }
                    else if (line > beginLine && line < endLine)
                    {
                        sb.append(ln);
                    }
                    else if (line == beginLine)
                    {
                        sb.append(ln);
                    }
                    else
                    {
                        sb.append(ln);
                        break;
                    }
                }

                line++;
            }

            br.close();
        }
        catch (IOException ioex)
        {
            ioex.printStackTrace();
        }

        return sb.toString();
    }

    /**
     * Generates parameters names list and information list from a list of
     * Parameter objects
     * 
     * @param parameters
     *            List of Parameter objects to be transformed
     * @param params
     *            List of paremeter names to be filled in
     * @param paramInfo
     *            List of parameter information object to be filled in
     */
    private void getParams(List<Parameter> parameters, List<String> params, List<TypePair> paramInfo)
    {
        if (parameters == null || parameters.isEmpty())
            return;

        List<Type> types = new ArrayList<Type>();
        for (int i = 0; i < parameters.size(); i++)
        {
            params.add(parameters.get(i).getId().getName());
            types.add(parameters.get(i).getType());
        }

        paramInfo = getParamTypes(types);
    }

    /**
     * Generates a list of parameter type information from a list of Type
     * objects
     * 
     * @param types
     *            list of Types to be transformed
     * @return List of TypePair information for a set of method parameters
     */
    private List<TypePair> getParamTypes(List<Type> types)
    {
        List<TypePair> paramInfo = new ArrayList<TypePair>();
        if (types == null || types.isEmpty())
            return paramInfo;

        for (int i = 0; i < types.size(); i++)
        {
            Type type = types.get(i);
            boolean isPrimitive = false;
            String retType = "";

            if (type instanceof PrimitiveType)
            {
                PrimitiveType pType = (PrimitiveType) type;
                retType = pType.getType().toString();
                isPrimitive = true;
            }
            else if (type instanceof ClassOrInterfaceType)
            {
                ClassOrInterfaceType clType = (ClassOrInterfaceType) type;
                retType = clType.getName();
            }

            paramInfo.add(new TypePair(retType, isPrimitive));
        }

        return paramInfo;
    }

    /**
     * FIXME: Requires a massive refactoring to get rid of the if statements
     * Process a Statement object and generates a statment node.
     * 
     * @param stmt
     *            Statement object to be transformed into a StatementNode
     * @param node
     *            Parent of the newly formed StatementNode
     * @param stmtGraph
     *            graph into which the new statement node should be placed
     * @return the newly formed StatementNode
     */
    private StatementNode processStatement(Statement stmt, CodeNode node, CodeGraph stmtGraph)
    {
        if (stmt instanceof BlockStmt)
        {
            BlockStmt block = (BlockStmt) stmt;

            List<Statement> stmts = block.getStmts();
            StatementNode sNode = addStatement(
                    node,
                    stmtGraph,
                    StatementType.BLOCK,
                    siphonCode(file, block.getBeginLine(), block.getBeginColumn(), block.getEndLine(),
                            block.getEndColumn()));

            StatementNode lastStmt = sNode;

            if (stmts != null)
            {
                for (Statement s : stmts)
                {
                    StatementNode nextStmt = handleBodyStatements(s, lastStmt, stmtGraph);
                    lastStmt = nextStmt;

                    stmtGraph.insert(nextStmt);
                    stmtGraph.insert(new Edge(lastStmt, nextStmt, EdgeType.Statement));
                }
            }

            return lastStmt;
        }
        else if (stmt instanceof DoStmt)
        {
            DoStmt doStmt = (DoStmt) stmt;

            StatementNode sNode = addStatement(
                    node,
                    stmtGraph,
                    StatementType.DO_WHILE,
                    siphonCode(file, doStmt.getBeginLine(), doStmt.getBeginColumn(), doStmt.getEndLine(),
                            doStmt.getEndColumn()));

            StatementNode body = handleBodyStatements(doStmt.getBody(), sNode, stmtGraph);

            StatementNode condition = handleExpressionContents(doStmt.getCondition(), body, stmtGraph);
            if (condition != null)
            {
                stmtGraph.insert(condition);

                if (node instanceof StatementNode)
                {
                    stmtGraph.insert(new Edge(node, sNode, EdgeType.Statement));
                    sNode.setParent(((StatementNode) node).getParent());
                }
                else if (node instanceof MethodNode)
                {
                    sNode.setParent((MethodNode) node);
                }

                stmtGraph.insert(new Edge(body, sNode, EdgeType.Statement));
            }

            return condition;
        }
        else if (stmt instanceof ExpressionStmt)
        {
            ExpressionStmt exprStmt = (ExpressionStmt) stmt;
            Expression expr = exprStmt.getExpression();

            StatementNode exprNode = handleExpressionContents(expr, node, stmtGraph);

            return exprNode;
        }
        else if (stmt instanceof ForeachStmt)
        {
            ForeachStmt foreach = (ForeachStmt) stmt;

            StatementNode varInitStmt = handleExpressionContents(foreach.getVariable(), node, stmtGraph);
            StatementNode iterStmt = handleExpressionContents(foreach.getIterable(), varInitStmt, stmtGraph);
            StatementNode bodyStmt = handleBodyStatements(foreach.getBody(), iterStmt, stmtGraph);

            stmtGraph.insert(new Edge(bodyStmt, iterStmt, EdgeType.Statement));

            return iterStmt;
        }
        else if (stmt instanceof IfStmt)
        {
            IfStmt ifStmt = (IfStmt) stmt;

            StatementNode condNode = handleExpressionContents(ifStmt.getCondition(), node, stmtGraph);
            StatementNode elseNode = handleBodyStatements(ifStmt.getElseStmt(), condNode, stmtGraph);
            StatementNode thenNode = handleBodyStatements(ifStmt.getThenStmt(), condNode, stmtGraph);

            StatementNode endNode = addStatement(node, stmtGraph, StatementType.NULL_TYPE, "");
            stmtGraph.insert(new Edge(thenNode, endNode, EdgeType.Statement));
            stmtGraph.insert(new Edge(elseNode, endNode, EdgeType.Statement));

            return endNode;
        }
        else if (stmt instanceof ReturnStmt)
        {
            ReturnStmt retStmt = (ReturnStmt) stmt;
            StatementNode sNode = addStatement(
                    node,
                    stmtGraph,
                    StatementType.RETURN,
                    siphonCode(file, retStmt.getBeginLine(), retStmt.getBeginColumn(), retStmt.getEndLine(),
                            retStmt.getEndColumn()));

            return sNode;
        }
        else if (stmt instanceof SwitchEntryStmt)
        {
            SwitchEntryStmt switchEntry = (SwitchEntryStmt) stmt;

            StatementNode caseLabel = handleExpressionContents(switchEntry.getLabel(), node, stmtGraph);
            List<Statement> switchBody = switchEntry.getStmts();

            StatementNode lastStmt = caseLabel;
            if (switchBody != null)
            {
                for (Statement bodyStmt : switchBody)
                {
                    StatementNode nextStmt = handleBodyStatements(bodyStmt, lastStmt, stmtGraph);
                    lastStmt = nextStmt;
                }
            }

            return lastStmt;
        }
        else if (stmt instanceof SwitchStmt)
        {
            SwitchStmt swtch = (SwitchStmt) stmt;

            StatementNode selector = handleExpressionContents(swtch.getSelector(), node, stmtGraph);
            StatementNode endStmt = addStatement(node, stmtGraph, StatementType.NULL_TYPE, "");
            endStmt.setParent(selector.getParent());

            List<SwitchEntryStmt> caseStmts = swtch.getEntries();
            for (SwitchEntryStmt entry : caseStmts)
            {
                StatementNode caseStmt = handleBodyStatements(entry, selector, stmtGraph);
                stmtGraph.insert(new Edge(caseStmt, endStmt, EdgeType.Statement));
            }

            return endStmt;
        }
        else if (stmt instanceof SynchronizedStmt)
        {
            SynchronizedStmt sync = (SynchronizedStmt) stmt;

            StatementNode exprContent = handleExpressionContents(sync.getExpr(), node, stmtGraph);
            StatementNode bodyStmt = handleBodyStatements(sync.getBlock(), exprContent, stmtGraph);

            return bodyStmt;
        }
        else if (stmt instanceof ThrowStmt)
        {
            ThrowStmt throwStmt = (ThrowStmt) stmt;
            StatementNode sNode = addStatement(
                    node,
                    stmtGraph,
                    StatementType.THROW,
                    siphonCode(file, throwStmt.getBeginLine(), throwStmt.getBeginColumn(), throwStmt.getEndLine(),
                            throwStmt.getEndColumn()));

            handleExpressionContents(throwStmt.getExpr(), sNode, stmtGraph);

            return sNode;
        }
        else if (stmt instanceof TryStmt)
        {
            TryStmt tryStmt = (TryStmt) stmt;
            StatementNode sNode = addStatement(
                    node,
                    stmtGraph,
                    StatementType.TRY,
                    siphonCode(file, tryStmt.getBeginLine(), tryStmt.getBeginColumn(), tryStmt.getEndLine(),
                            tryStmt.getEndColumn()));

            StatementNode blockStmt = handleBodyStatements(tryStmt.getTryBlock(), sNode, stmtGraph);
            StatementNode finallyStmt = handleBodyStatements(tryStmt.getFinallyBlock(), blockStmt, stmtGraph);

            StatementNode endStmt = addStatement(node, stmtGraph, StatementType.NULL_TYPE, "");
            if (node instanceof StatementNode)
            {
                endStmt.setParent(((StatementNode) node).getParent());
            }
            else if (node instanceof MethodNode)
            {
                endStmt.setParent((MethodNode) node);
            }

            stmtGraph.insert(new Edge(finallyStmt, endStmt, EdgeType.Statement));

            List<CatchClause> catches = tryStmt.getCatchs();
            if (catches != null)
            {
                for (CatchClause cc : catches)
                {
                    BlockStmt catchBlock = cc.getCatchBlock();
                    StatementNode catchStmt = handleBodyStatements(catchBlock, blockStmt, stmtGraph);
                    stmtGraph.insert(new Edge(catchStmt, endStmt, EdgeType.Statement));
                }
            }
            return endStmt;
        }
        else if (stmt instanceof TypeDeclarationStmt)
        {
            TypeDeclarationStmt typeDec = (TypeDeclarationStmt) stmt;
            StatementNode sNode = addStatement(
                    node,
                    stmtGraph,
                    StatementType.TYPE_DECLARATION,
                    siphonCode(file, typeDec.getBeginLine(), typeDec.getBeginColumn(), typeDec.getEndLine(),
                            typeDec.getEndColumn()));

            handleTypeDeclaration(typeDec.getTypeDeclaration(), sNode);

            return sNode;
        }
        else if (stmt instanceof WhileStmt)
        {
            WhileStmt whileStmt = (WhileStmt) stmt;

            StatementNode condStmt = handleExpressionContents(whileStmt.getCondition(), node, stmtGraph);
            StatementNode bodyStmt = handleBodyStatements(whileStmt.getBody(), condStmt, stmtGraph);

            stmtGraph.insert(new Edge(bodyStmt, condStmt, EdgeType.Statement));

            return condStmt;
        }
        else
        {
            return addStatement(node, stmtGraph, StatementType.NULL_TYPE, "");
        }
    }

    /*
     * (non-Javadoc)
     * @see
     * truerefactor.input.GraphParser#addStatement(truerefactor.graph.CodeNode,
     * java.lang.String, java.lang.String)
     */
    @Override
    public StatementNode addStatement(CodeNode parent, CodeGraph stmtGraph, StatementType type, String code)
    {
        MethodNode pMNode = null;
        if (parent instanceof StatementNode)
        {
            pMNode = ((StatementNode) parent).getParent();
        }
        else if (parent instanceof MethodNode)
        {
            pMNode = (MethodNode) parent;
        }

        StatementNode sNode = new StatementNode(pMNode, type, code);

        stmtGraph.insert(sNode);

        if (parent instanceof StatementNode)
        {
            if (sNode != null && parent != null)
                stmtGraph.insert(new Edge(parent, sNode, EdgeType.Statement));
        }

        return sNode;
    }

    public int getLinesOfCode(File file)
    {
        int loc = 0;

        try
        {
            if (file.exists() && file.canRead())
            {
                FileReader fr = new FileReader(file);
                BufferedReader br = new BufferedReader(fr);

                String line;
                boolean mlc = false;
                while ((line = br.readLine()) != null)
                {
                    line = line.trim();
                    if (line.isEmpty())
                    {
                        continue;
                    }

                    if (line.startsWith("/*"))
                    {
                        mlc = true;
                    }

                    if (line.contains("*/"))
                    {
                        mlc = false;
                    }

                    if (mlc)
                    {
                        continue;
                    }

                    if (line.startsWith("//"))
                    {
                        continue;
                    }

                    loc++;
                }

                br.close();
                fr.close();
            }
        }
        catch (IOException ioex)
        {
            loc = 0;
        }

        return loc;
    }
}
